/*
	SPDX-License-Identifier: GPL-2.0
	Modified from src/kernel/memaccess.c in https://github.com/NVSL/OptaneStudy
*/

#include "access.h"
#include "common.h"

/* ============== Sequential read and write test function ============== */
void seq_read(char * start_addr, long size, long count) {
	asm volatile (
		"mov %[start_addr], %%r9 \n"

"LOOP_SEQLOAD1: \n"
		"xor %%r8, %%r8 \n"
"LOOP_SEQLOAD2: \n"
		"vmovntdqa 0x0(%%r9, %%r8), %%zmm0 \n"
		"add $0x40, %%r8 \n"
		"cmp %[size], %%r8 \n"
		"jl LOOP_SEQLOAD2 \n"

		"add %[size], %%r9 \n"
		"cmp %[end_addr], %%r9 \n"
		"jl LOOP_SEQLOAD1 \n"

		:: [start_addr]"r"(start_addr), [end_addr]"r"(start_addr + count * size), [size]"r"(size)
		:"%r8", "%r9"
	);

}

void seq_rw41(char * start_addr, long size, long count) {
    char * end_addr = start_addr + count * size;

    asm volatile (
		"mov %[start_addr], %%r9 \n"
		"movq %[start_addr], %%xmm0 \n"

"LOOP_SRW41_OUTER: \n"
		"xor %%r8, %%r8 \n"

"LOOP_SRW41_SEQLOAD1: \n"
		"vmovntdqa 0x0(%%r9, %%r8), %%zmm0 \n"
		"add $0x40, %%r8 \n"
		"cmp %[size], %%r8 \n"
		"jl LOOP_SRW41_SEQLOAD1 \n"
        
		"add %[size], %%r9 \n"
		"xor %%r8, %%r8 \n"

"LOOP_SRW41_SEQLOAD2: \n"
		"vmovntdqa 0x0(%%r9, %%r8), %%zmm0 \n"
		"add $0x40, %%r8 \n"
		"cmp %[size], %%r8 \n"
		"jl LOOP_SRW41_SEQLOAD2 \n"
        
		"add %[size], %%r9 \n"
		"xor %%r8, %%r8 \n"

"LOOP_SRW41_SEQLOAD3: \n"
		"vmovntdqa 0x0(%%r9, %%r8), %%zmm0 \n"
		"add $0x40, %%r8 \n"
		"cmp %[size], %%r8 \n"
		"jl LOOP_SRW41_SEQLOAD3 \n"

		"add %[size], %%r9 \n"
		"xor %%r8, %%r8 \n"

"LOOP_SRW41_SEQLOAD4: \n"
		"vmovntdqa 0x0(%%r9, %%r8), %%zmm0 \n"
		"add $0x40, %%r8 \n"
		"cmp %[size], %%r8 \n"
		"jl LOOP_SRW41_SEQLOAD4 \n"
        
		"add %[size], %%r9 \n"
		"xor %%r8, %%r8 \n"

"LOOP_SRW41_SEQCLWB1: \n"
		"vmovdqa64  %%zmm0,  0x0(%%r9, %%r8) \n"
		"clwb  (%%r9, %%r8) \n"
		"add $0x40, %%r8 \n"
		"cmp %[size], %%r8 \n"
		"jl LOOP_SRW41_SEQCLWB1 \n"
        
		"add %[size], %%r9 \n"
		"cmp %[end_addr], %%r9 \n"
		"jl LOOP_SRW41_OUTER \n"

		:: [start_addr]"r"(start_addr), [end_addr]"r"(end_addr), [size]"r"(size)
		:  "%r8", "%r9"
	);
}

void seq_rw21(char * start_addr, long size, long count) {
    char * end_addr = start_addr + count * size;

    asm volatile (
		"mov %[start_addr], %%r9 \n"
		"movq %[start_addr], %%xmm0 \n"

"LOOP_SRW21_OUTER: \n"
		"xor %%r8, %%r8 \n"

"LOOP_SRW21_SEQLOAD1: \n"
		"vmovntdqa 0x0(%%r9, %%r8), %%zmm0 \n"
		"add $0x40, %%r8 \n"
		"cmp %[size], %%r8 \n"
		"jl LOOP_SRW21_SEQLOAD1 \n"
        
		"add %[size], %%r9 \n"
		"xor %%r8, %%r8 \n"

"LOOP_SRW21_SEQLOAD2: \n"
		"vmovntdqa 0x0(%%r9, %%r8), %%zmm0 \n"
		"add $0x40, %%r8 \n"
		"cmp %[size], %%r8 \n"
		"jl LOOP_SRW21_SEQLOAD2 \n"
        
		"add %[size], %%r9 \n"
		"xor %%r8, %%r8 \n"

"LOOP_SRW21_SEQCLWB1: \n"
		"vmovdqa64  %%zmm0,  0x0(%%r9, %%r8) \n"
		"clwb  (%%r9, %%r8) \n"
		"add $0x40, %%r8 \n"
		"cmp %[size], %%r8 \n"
		"jl LOOP_SRW21_SEQCLWB1 \n"
        
		"add %[size], %%r9 \n"
		"cmp %[end_addr], %%r9 \n"
		"jl LOOP_SRW21_OUTER \n"

		:: [start_addr]"r"(start_addr), [end_addr]"r"(end_addr), [size]"r"(size)
		: "%r8", "%r9"
	);
}

void seq_rw11(char * start_addr, long size, long count) {
    char * end_addr = start_addr + count * size;

    asm volatile (
		"mov %[start_addr], %%r9 \n"
		"movq %[start_addr], %%xmm0 \n"

"LOOP_SRW11_OUTER: \n"
		"xor %%r8, %%r8 \n"

"LOOP_SRW11_SEQLOAD1: \n"
		"vmovntdqa 0x0(%%r9, %%r8), %%zmm0 \n"
		"add $0x40, %%r8 \n"
		"cmp %[size], %%r8 \n"
		"jl LOOP_SRW11_SEQLOAD1 \n"
        
		"add %[size], %%r9 \n"
		"xor %%r8, %%r8 \n"

"LOOP_SRW11_SEQLOAD2: \n"
		"vmovntdqa 0x0(%%r9, %%r8), %%zmm0 \n"
		"add $0x40, %%r8 \n"
		"cmp %[size], %%r8 \n"
		"jl LOOP_SRW11_SEQLOAD2 \n"
        
		"add %[size], %%r9 \n"
		"xor %%r8, %%r8 \n"

"LOOP_SRW11_SEQCLWB1: \n"
		"vmovdqa64  %%zmm0,  0x0(%%r9, %%r8) \n"
		"clwb  (%%r9, %%r8) \n"
		"add $0x40, %%r8 \n"
		"cmp %[size], %%r8 \n"
		"jl LOOP_SRW11_SEQCLWB1 \n"
        
		"add %[size], %%r9 \n"
		"xor %%r8, %%r8 \n"

"LOOP_SRW11_SEQCLWB2: \n"
		"vmovdqa64  %%zmm0,  0x0(%%r9, %%r8) \n"
		"clwb  (%%r9, %%r8) \n"
		"add $0x40, %%r8 \n"
		"cmp %[size], %%r8 \n"
		"jl LOOP_SRW11_SEQCLWB2 \n"
        
		"add %[size], %%r9 \n"
		"cmp %[end_addr], %%r9 \n"
		"jl LOOP_SRW11_OUTER \n"

		:: [start_addr]"r"(start_addr), [end_addr]"r"(end_addr), [size]"r"(size)
		:  "%r8", "%r9"
	);
}

void seq_rw12(char * start_addr, long size, long count) {
    char * end_addr = start_addr + count * size;

    asm volatile (
		"mov %[start_addr], %%r9 \n"
		"movq %[start_addr], %%xmm0 \n"

"LOOP_SRW12_OUTER: \n"
		"xor %%r8, %%r8 \n"

"LOOP_SRW12_SEQLOAD1: \n"
		"vmovntdqa 0x0(%%r9, %%r8), %%zmm0 \n"
		"add $0x40, %%r8 \n"
		"cmp %[size], %%r8 \n"
		"jl LOOP_SRW12_SEQLOAD1 \n"
        
		"add %[size], %%r9 \n"
		"xor %%r8, %%r8 \n"

"LOOP_SRW12_SEQCLWB1: \n"
		"vmovdqa64  %%zmm0,  0x0(%%r9, %%r8) \n"
		"clwb  (%%r9, %%r8) \n"
		"add $0x40, %%r8 \n"
		"cmp %[size], %%r8 \n"
		"jl LOOP_SRW12_SEQCLWB1 \n"

		"add %[size], %%r9 \n"
		"xor %%r8, %%r8 \n"

"LOOP_SRW12_SEQCLWB2: \n"
		"vmovdqa64  %%zmm0,  0x0(%%r9, %%r8) \n"
		"clwb  (%%r9, %%r8) \n"
		"add $0x40, %%r8 \n"
		"cmp %[size], %%r8 \n"
		"jl LOOP_SRW12_SEQCLWB2 \n"

        "add %[size], %%r9 \n"
		"cmp %[end_addr], %%r9 \n"
		"jl LOOP_SRW12_OUTER \n"

		:: [start_addr]"r"(start_addr), [end_addr]"r"(end_addr), [size]"r"(size):
			"%r8", "%r9");
}

void seq_rw14(char * start_addr, long size, long count) {
    char * end_addr = start_addr + count * size;
    
    asm volatile (
		"mov %[start_addr], %%r9 \n"
		"movq %[start_addr], %%xmm0 \n"

"LOOP_SRW14_OUTER: \n"
		"xor %%r8, %%r8 \n"

"LOOP_SRW14_SEQLOAD1: \n"
		"vmovntdqa 0x0(%%r9, %%r8), %%zmm0 \n"
		"add $0x40, %%r8 \n"
		"cmp %[size], %%r8 \n"
		"jl LOOP_SRW14_SEQLOAD1 \n"
        
		"add %[size], %%r9 \n"
		"xor %%r8, %%r8 \n"

"LOOP_SRW14_SEQCLWB1: \n"
		"vmovdqa64  %%zmm0,  0x0(%%r9, %%r8) \n"
		"clwb  (%%r9, %%r8) \n"
		"add $0x40, %%r8 \n"
		"cmp %[size], %%r8 \n"
		"jl LOOP_SRW14_SEQCLWB1 \n"
        
		"add %[size], %%r9 \n"
		"xor %%r8, %%r8 \n"

"LOOP_SRW14_SEQCLWB2: \n"
		"vmovdqa64  %%zmm0,  0x0(%%r9, %%r8) \n"
		"clwb  (%%r9, %%r8) \n"
		"add $0x40, %%r8 \n"
		"cmp %[size], %%r8 \n"
		"jl LOOP_SRW14_SEQCLWB2 \n"
        
		"add %[size], %%r9 \n"
		"xor %%r8, %%r8 \n"

"LOOP_SRW14_SEQCLWB3: \n"
		"vmovdqa64  %%zmm0,  0x0(%%r9, %%r8) \n"
		"clwb  (%%r9, %%r8) \n"
		"add $0x40, %%r8 \n"
		"cmp %[size], %%r8 \n"
		"jl LOOP_SRW14_SEQCLWB3 \n"
        
		"add %[size], %%r9 \n"
		"xor %%r8, %%r8 \n"

"LOOP_SRW14_SEQCLWB4: \n"
		"vmovdqa64  %%zmm0,  0x0(%%r9, %%r8) \n"
		"clwb  (%%r9, %%r8) \n"
		"add $0x40, %%r8 \n"
		"cmp %[size], %%r8 \n"
		"jl LOOP_SRW14_SEQCLWB4 \n"
        
		"add %[size], %%r9 \n"
		"cmp %[end_addr], %%r9 \n"
		"jl LOOP_SRW14_OUTER \n"

		:: [start_addr]"r"(start_addr), [end_addr]"r"(end_addr), [size]"r"(size)
		: "%r8", "%r9"
	);
}

void seq_write(char * start_addr, long size, long count) {
    asm volatile (
		"mov %[start_addr], %%r9 \n"
		"movq %[start_addr], %%xmm0 \n"

"LOOP_SEQCLWB1: \n"
		"xor %%r8, %%r8 \n"
"LOOP_SEQCLWB2: \n"
		"vmovdqa64  %%zmm0,  0x0(%%r9, %%r8) \n"
		"clwb  (%%r9, %%r8) \n"
		"add $0x40, %%r8 \n"
		"cmp %[size], %%r8 \n"
		"jl LOOP_SEQCLWB2 \n"

		"add %[size], %%r9 \n"
		"cmp %[end_addr], %%r9 \n"
		"jl LOOP_SEQCLWB1 \n"

		:: [start_addr]"r"(start_addr), [end_addr]"r"(start_addr + count * size), [size]"r"(size)
		: "%r8", "%r9"
	);
}

/* ============== random read and write test function ==============*/
void rand_read(char * start_addr, long size, long count) {
	long randnum = getRandom();
	long * rand_seed = &randnum;
	
	static long access_mask = (BUFSIZE - 1) ^ (ALIGN_SIZE- 1);
    
	asm volatile (
		"movabs $0xd800000000000000, %%rcx \n"	/* rcx: bitmask used in LFSR */
		"xor %%r8, %%r8 \n"						/* r8: access offset */
		"xor %%r11, %%r11 \n"					/* r11: access counter */
// 1
"LOOP_FRNG_SIZEBWL_RLOOP: \n"					/* outer (counter) loop */
		RandLFSR64								/* LFSR: uses r9, r12 (reusable), rcx (above), fill r8 */
		"lea (%[start_addr], %%r8), %%r9 \n"
		"xor %%r10, %%r10 \n"					/* r10: accessed size */
"LOOP_FRNG_SIZEBWL_ONE1: \n"					/* inner (access) loop, unroll 8 times */
		LD_AVX512							/* Access: uses r8[rand_base], r10[size_accessed], r9 */
		"cmp %[accesssize], %%r10 \n"
		"jl LOOP_FRNG_SIZEBWL_ONE1 \n"
// 2
		RandLFSR64
		"lea (%[start_addr], %%r8), %%r9 \n"
		"xor %%r10, %%r10 \n"
"LOOP_FRNG_SIZEBWL_ONE2: \n"
		LD_AVX512
		"cmp %[accesssize], %%r10 \n"
		"jl LOOP_FRNG_SIZEBWL_ONE2 \n"
// 3
		RandLFSR64
		"lea (%[start_addr], %%r8), %%r9 \n"
		"xor %%r10, %%r10 \n"   
"LOOP_FRNG_SIZEBWL_ONE3: \n"
		LD_AVX512
		"cmp %[accesssize], %%r10 \n"
		"jl LOOP_FRNG_SIZEBWL_ONE3 \n"
// 4
		RandLFSR64
		"lea (%[start_addr], %%r8), %%r9 \n"
		"xor %%r10, %%r10 \n"
"LOOP_FRNG_SIZEBWL_ONE4: \n"
		LD_AVX512
		"cmp %[accesssize], %%r10 \n"
		"jl LOOP_FRNG_SIZEBWL_ONE4 \n"

		"add $4, %%r11 \n"
		"cmp %[count], %%r11\n"
		"jl LOOP_FRNG_SIZEBWL_RLOOP \n"

		: [random]"=r"(rand_seed)
		: [start_addr]"r"(start_addr), [accesssize]"r"(size), [count]"r"(count), "0"(rand_seed), [accessmask]"r"(access_mask)
		: "%rcx", "%r12", "%r11", "%r10", "%r9", "%r8"
	);
}

void rand_write(char * start_addr, long size, long count) {
	long randnum = getRandom();
	long * rand_seed = &randnum;
	static long access_mask = (BUFSIZE - 1) ^ (ALIGN_SIZE- 1);

	asm volatile (
		"movabs $0xd800000000000000, %%rcx \n"
		"xor %%r11, %%r11 \n"
		"movq %[start_addr], %%xmm0 \n"			/* zmm0: read/write register */
// 1
"LOOP_FRNG_SIZEBWSTFLUSH_RLOOP: \n"
		RandLFSR64
		"lea (%[start_addr], %%r8), %%r9 \n"
		"xor %%r10, %%r10 \n"
"LOOP_FRNG_SIZEBWSTFLUSH_ONE1: \n"
		WR_AVX512
		"cmp %[accesssize], %%r10 \n"
		"jl LOOP_FRNG_SIZEBWSTFLUSH_ONE1 \n"
		BW_FENCE
// 2
		RandLFSR64
		"lea (%[start_addr], %%r8), %%r9 \n"
		"xor %%r10, %%r10 \n"
"LOOP_FRNG_SIZEBWSTFLUSH_ONE2: \n"
		WR_AVX512
		"cmp %[accesssize], %%r10 \n"
		"jl LOOP_FRNG_SIZEBWSTFLUSH_ONE2 \n"
		BW_FENCE
// 3
		RandLFSR64
		"lea (%[start_addr], %%r8), %%r9 \n"
		"xor %%r10, %%r10 \n"
"LOOP_FRNG_SIZEBWSTFLUSH_ONE3: \n"
		WR_AVX512
		"cmp %[accesssize], %%r10 \n"
		"jl LOOP_FRNG_SIZEBWSTFLUSH_ONE3 \n"
		BW_FENCE
// 4
		RandLFSR64
		"lea (%[start_addr], %%r8), %%r9 \n"
		"xor %%r10, %%r10 \n"
"LOOP_FRNG_SIZEBWSTFLUSH_ONE4: \n"
		WR_AVX512
		"cmp %[accesssize], %%r10 \n"
		"jl LOOP_FRNG_SIZEBWSTFLUSH_ONE4 \n"
		BW_FENCE

		"add $4, %%r11 \n"
		"cmp %[count], %%r11\n"
		"jl LOOP_FRNG_SIZEBWSTFLUSH_RLOOP \n"

		: [random]"=r"(rand_seed)
		: [start_addr]"r"(start_addr), [accesssize]"r"(size), [count]"r"(count), "0"(rand_seed), [accessmask]"r"(access_mask)
		: "%rcx", "%r12", "%r11", "%r10", "%r9", "%r8"
	);
}

void rand_rw41(char * start_addr, long size, long count) {
	long randnum = getRandom();
	long * rand_seed = &randnum;
	static long access_mask = (BUFSIZE - 1) ^ (ALIGN_SIZE- 1);

	asm volatile (
		"movabs $0xd800000000000000, %%rcx \n"
		"xor %%r11, %%r11 \n"
		"movq %[start_addr], %%xmm0 \n"			/* zmm0: read/write register */
"LOOP_RRW41_LOOP: \n"
// read 1	
		RandLFSR64
		"lea (%[start_addr], %%r8), %%r9 \n"
		"xor %%r10, %%r10 \n"
"LOOP_RRW41_RD1: \n"
		LD_AVX512
		"cmp %[accesssize], %%r10 \n"
		"jl LOOP_RRW41_RD1 \n"
// read 2
		RandLFSR64
		"lea (%[start_addr], %%r8), %%r9 \n"
		"xor %%r10, %%r10 \n"
"LOOP_RRW41_RD2: \n"
		LD_AVX512
		"cmp %[accesssize], %%r10 \n"
		"jl LOOP_RRW41_RD2 \n"
// read 3	
		RandLFSR64
		"lea (%[start_addr], %%r8), %%r9 \n"
		"xor %%r10, %%r10 \n"
"LOOP_RRW41_RD3: \n"
		LD_AVX512
		"cmp %[accesssize], %%r10 \n"
		"jl LOOP_RRW41_RD3 \n"
// read 4
		RandLFSR64
		"lea (%[start_addr], %%r8), %%r9 \n"
		"xor %%r10, %%r10 \n"
"LOOP_RRW41_RD4: \n"
		LD_AVX512
		"cmp %[accesssize], %%r10 \n"
		"jl LOOP_RRW41_RD4 \n"
// write 1
		RandLFSR64
		"lea (%[start_addr], %%r8), %%r9 \n"
		"xor %%r10, %%r10 \n"
"LOOP_RRW41_WR1: \n"
		WR_AVX512
		"cmp %[accesssize], %%r10 \n"
		"jl LOOP_RRW41_WR1 \n"
		BW_FENCE
		
		"add $5, %%r11 \n"
		"cmp %[count], %%r11\n"
		"jl LOOP_RRW41_LOOP \n"

		: [random]"=r"(rand_seed)
		: [start_addr]"r"(start_addr), [accesssize]"r"(size), [count]"r"(count), "0"(rand_seed), [accessmask]"r"(access_mask)
		: "%rcx", "%r12", "%r11", "%r10", "%r9", "%r8"
	);
}

void rand_rw21(char * start_addr, long size, long count) {
	long randnum = getRandom();
	long * rand_seed = &randnum;
	static long access_mask = (BUFSIZE - 1) ^ (ALIGN_SIZE- 1);

	asm volatile (
		"movabs $0xd800000000000000, %%rcx \n"
		"xor %%r11, %%r11 \n"
		"movq %[start_addr], %%xmm0 \n"			/* zmm0: read/write register */
"LOOP_RRW21_LOOP: \n"
// read 1	
		RandLFSR64
		"lea (%[start_addr], %%r8), %%r9 \n"
		"xor %%r10, %%r10 \n"
"LOOP_RRW21_RD1: \n"
		LD_AVX512
		"cmp %[accesssize], %%r10 \n"
		"jl LOOP_RRW21_RD1 \n"
// read 2
		RandLFSR64
		"lea (%[start_addr], %%r8), %%r9 \n"
		"xor %%r10, %%r10 \n"
"LOOP_RRW21_RD2: \n"
		LD_AVX512
		"cmp %[accesssize], %%r10 \n"
		"jl LOOP_RRW21_RD2 \n"
// write 1
		RandLFSR64
		"lea (%[start_addr], %%r8), %%r9 \n"
		"xor %%r10, %%r10 \n"
"LOOP_RRW21_WR1: \n"
		WR_AVX512
		"cmp %[accesssize], %%r10 \n"
		"jl LOOP_RRW21_WR1 \n"
		BW_FENCE
		
		"add $3, %%r11 \n"
		"cmp %[count], %%r11\n"
		"jl LOOP_RRW21_LOOP \n"

		: [random]"=r"(rand_seed)
		: [start_addr]"r"(start_addr), [accesssize]"r"(size), [count]"r"(count), "0"(rand_seed), [accessmask]"r"(access_mask)
		: "%rcx", "%r12", "%r11", "%r10", "%r9", "%r8"
	);
}

void rand_rw11(char * start_addr, long size, long count) {
	long randnum = getRandom();
	long * rand_seed = &randnum;
	static long access_mask = (BUFSIZE - 1) ^ (ALIGN_SIZE- 1);

	asm volatile (
		"movabs $0xd800000000000000, %%rcx \n"
		"xor %%r11, %%r11 \n"
		"movq %[start_addr], %%xmm0 \n"			/* zmm0: read/write register */
"LOOP_RRW11_LOOP: \n"
// read 1	
		RandLFSR64
		"lea (%[start_addr], %%r8), %%r9 \n"
		"xor %%r10, %%r10 \n"
"LOOP_RRW11_RD1: \n"
		LD_AVX512
		"cmp %[accesssize], %%r10 \n"
		"jl LOOP_RRW11_RD1 \n"
// read 2	
		RandLFSR64
		"lea (%[start_addr], %%r8), %%r9 \n"
		"xor %%r10, %%r10 \n"
"LOOP_RRW11_RD2: \n"
		LD_AVX512
		"cmp %[accesssize], %%r10 \n"
		"jl LOOP_RRW11_RD2 \n"


// write 1
		RandLFSR64
		"lea (%[start_addr], %%r8), %%r9 \n"
		"xor %%r10, %%r10 \n"
"LOOP_RRW11_WR1: \n"
		WR_AVX512
		"cmp %[accesssize], %%r10 \n"
		"jl LOOP_RRW11_WR1 \n"
		BW_FENCE
// write 2
		RandLFSR64
		"lea (%[start_addr], %%r8), %%r9 \n"
		"xor %%r10, %%r10 \n"
"LOOP_RRW11_WR2: \n"
		WR_AVX512
		"cmp %[accesssize], %%r10 \n"
		"jl LOOP_RRW11_WR2 \n"
		BW_FENCE
		
		"add $4, %%r11 \n"
		"cmp %[count], %%r11\n"
		"jl LOOP_RRW11_LOOP \n"

		: [random]"=r"(rand_seed)
		: [start_addr]"r"(start_addr), [accesssize]"r"(size), [count]"r"(count), "0"(rand_seed), [accessmask]"r"(access_mask)
		: "%rcx", "%r12", "%r11", "%r10", "%r9", "%r8");
}

void rand_rw12(char * start_addr, long size, long count) {
	long randnum = getRandom();
	long * rand_seed = &randnum;
	static long access_mask = (BUFSIZE - 1) ^ (ALIGN_SIZE- 1);

	asm volatile (
		"movabs $0xd800000000000000, %%rcx \n"
		"xor %%r11, %%r11 \n"
		"movq %[start_addr], %%xmm0 \n"			/* zmm0: read/write register */
"LOOP_RRW12_LOOP: \n"
// read 1	
		RandLFSR64
		"lea (%[start_addr], %%r8), %%r9 \n"
		"xor %%r10, %%r10 \n"
"LOOP_RRW12_RD1: \n"
		LD_AVX512
		"cmp %[accesssize], %%r10 \n"
		"jl LOOP_RRW12_RD1 \n"
// write 1
		RandLFSR64
		"lea (%[start_addr], %%r8), %%r9 \n"
		"xor %%r10, %%r10 \n"
"LOOP_RRW12_WR1: \n"
		WR_AVX512
		"cmp %[accesssize], %%r10 \n"
		"jl LOOP_RRW12_WR1 \n"
		BW_FENCE
// write 2
		RandLFSR64
		"lea (%[start_addr], %%r8), %%r9 \n"
		"xor %%r10, %%r10 \n"
"LOOP_RRW12_WR2: \n"
		WR_AVX512
		"cmp %[accesssize], %%r10 \n"
		"jl LOOP_RRW12_WR2 \n"
		BW_FENCE

		"add $3, %%r11 \n"
		"cmp %[count], %%r11\n"
		"jl LOOP_RRW12_LOOP \n"

		: [random]"=r"(rand_seed)
		: [start_addr]"r"(start_addr), [accesssize]"r"(size), [count]"r"(count), "0"(rand_seed), [accessmask]"r"(access_mask)
		: "%rcx", "%r12", "%r11", "%r10", "%r9", "%r8"
	);
}

void rand_rw14(char * start_addr, long size, long count) {
	long randnum = getRandom();
	long * rand_seed = &randnum;
	static long access_mask = (BUFSIZE - 1) ^ (ALIGN_SIZE- 1);

	asm volatile (
		"movabs $0xd800000000000000, %%rcx \n"
		"xor %%r11, %%r11 \n"
		"movq %[start_addr], %%xmm0 \n"			/* zmm0: read/write register */
"LOOP_RRW14_LOOP: \n"
// read 1	
		RandLFSR64
		"lea (%[start_addr], %%r8), %%r9 \n"
		"xor %%r10, %%r10 \n"
"LOOP_RRW14_RD1: \n"
		LD_AVX512
		"cmp %[accesssize], %%r10 \n"
		"jl LOOP_RRW14_RD1 \n"
// write 1
		RandLFSR64
		"lea (%[start_addr], %%r8), %%r9 \n"
		"xor %%r10, %%r10 \n"
"LOOP_RRW14_WR1: \n"
		WR_AVX512
		"cmp %[accesssize], %%r10 \n"
		"jl LOOP_RRW14_WR1 \n"
		BW_FENCE
// write 2
		RandLFSR64
		"lea (%[start_addr], %%r8), %%r9 \n"
		"xor %%r10, %%r10 \n"
"LOOP_RRW14_WR2: \n"
		WR_AVX512
		"cmp %[accesssize], %%r10 \n"
		"jl LOOP_RRW14_WR2 \n"
		BW_FENCE
// write 3
		RandLFSR64
		"lea (%[start_addr], %%r8), %%r9 \n"
		"xor %%r10, %%r10 \n"
"LOOP_RRW14_WR3: \n"
		WR_AVX512
		"cmp %[accesssize], %%r10 \n"
		"jl LOOP_RRW14_WR3 \n"
		BW_FENCE
// write 4
		RandLFSR64
		"lea (%[start_addr], %%r8), %%r9 \n"
		"xor %%r10, %%r10 \n"
"LOOP_RRW14_WR4: \n"
		WR_AVX512
		"cmp %[accesssize], %%r10 \n"
		"jl LOOP_RRW14_WR4 \n"
		BW_FENCE

		"add $5, %%r11 \n"
		"cmp %[count], %%r11\n"
		"jl LOOP_RRW14_LOOP \n"

		: [random]"=r"(rand_seed)
		: [start_addr]"r"(start_addr), [accesssize]"r"(size), [count]"r"(count), "0"(rand_seed), [accessmask]"r"(access_mask)
		: "%rcx", "%r12", "%r11", "%r10", "%r9", "%r8"
	);
}

void rand_write_lat(char * start_addr, long count) {
	long randnum = getRandom();
	long * rand_seed = &randnum;
	static long access_mask = (BUFSIZE - 1) ^ (ALIGN_SIZE - 1);

	asm volatile (
		"movabs $0xd800000000000000, %%rcx \n"
		"xor %%r11, %%r11 \n"
		"movq %[start_addr], %%xmm0 \n"			/* zmm0: read/write register */
		
"LOOP_FENCED_WRITE: \n"
		RandLFSR64								/* get a random value into r8*/
		"lea (%[start_addr], %%r8), %%r9 \n"
		"xor %%r10, %%r10 \n"
		WR_AVX512_2
		LAT_FENCE							/* fence each write operation*/

		"add $1, %%r11 \n"
		"cmp %[count], %%r11\n"
		"jl LOOP_FENCED_WRITE \n"

		: [random]"=r"(rand_seed)
		: [start_addr]"r"(start_addr), [count]"r"(count), "0"(rand_seed), [accessmask]"r"(access_mask)
		: "%rcx", "%r12", "%r11", "%r10", "%r9", "%r8"
	);
}